<?php
// +----------------------------------------------------------------------
// | zhanshop-cloud / TestBridgeClient.php    [ 2025/6/23 11:57 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2025 zhangqiquan All rights reserved.
// +----------------------------------------------------------------------
// | Author: zhangqiquan <768617998@qq.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace app\console;

use zhanshop\App;
use zhanshop\console\Command;
use zhanshop\console\Input;
use zhanshop\console\Output;
use zhanshop\Log;

class TestBridgeClient extends Command
{
    public $config = [
        'type' => 1, // SWOOLE_SOCK_TCP
        'ip' => "127.0.0.1",
        'port' => "8201",
        'path' => '/v1/bridge', // 桥接的建立连接和吞吐消息的path前缀
        'servfd' => 0, // 当前桥接服务对应服务端的fd
        'backlog' => 7168, // 最大情况 单个连接7168 * 64K = 约会占用500M内存
        'worker_id' => 0,
        'timeout' => 3,
        'debug' => false,
    ];
    public function configure()
    {
        $this->setTitle('测试桥连接')->setDescription('保持桥连接在线测试');
    }

    public function execute(Input $input, Output $output)
    {
        $config = App::config()->get('bridge');
        if($config) $this->config = array_merge($this->config, $config);
        $this->config['servfd'] = 0;
        $this->hostbyname();
        if($this->config['ip']){
            for (;;) {
                $this->listen();
                sleep($this->config['timeout']);
            }
        }
    }

    protected function hostbyname()
    {
        if(!filter_var($this->config['ip'], FILTER_VALIDATE_IP)){
            $list = dns_get_record($this->config['ip'], DNS_AAAA);
            if(isset($list[0]['ipv6'])){
                $this->config['ip'] = $list[0]['ipv6'];
            }else{
                $list = dns_get_record($this->config['ip'], DNS_A);
                if(isset($list[0]['ip'])) $this->config['ip'] = $list[0]['ip'];
            }

        }
    }

    protected function listen()
    {
        $domain = AF_INET6;
        if ($this->config['type'] != 3) {
            $domain = AF_INET;
        }
        $socket = socket_create($domain, SOCK_STREAM, SOL_TCP);
        $result = socket_connect($socket, $this->config['ip'], $this->config['port']);
        if ($result === false) {
            echo "连接失败";
            exit();
        }

        if ($this->establish($socket) == false) return;

        $this->eventLoop($socket); // 开始事件循环 读取和发送消息

        Log::errorLog(3, "桥服务tcp://[" . $this->config['ip'] . "]:" . $this->config['port'] . '连接被断开[' . $this->config['servfd'] . ']');

        socket_close($socket);
        $this->config['servfd'] = 0;
    }
    public function establish(\Socket $client)
    {
        $bridgName = $this->config['name'] ?? ($_SERVER['HOSTNAME'] ?? "unknown");
        $respData = [
            'uri' => $this->config['path'].'.establish', // 响应消息给服务端
            'header' => [
                'code' => 0,
                'msg' => "ok",
            ],
            'body' => [
                'name' => $bridgName,
                'version' => PHP_OS.':'.PHP_VERSION,
                'token' => $this->config['token'] ?? ''
            ],
        ];
        socket_write($client,json_encode($respData)."\r\n");
        $fd = socket_read($client, 1024);
        if(is_numeric($fd)){
            $fd = intval($fd);
            $this->config['servfd'] = $fd;
            Log::errorLog(3, "桥服务tcp://[".$this->config['ip']."]:".$this->config['port'].'连接建立成功['.$fd.']');
            return true;
        }else{
            $fd = str_replace(["\r", "\n"], '', $fd);
            Log::errorLog(5, "桥服务tcp://[".$this->config['ip']."]:".$this->config['port'].'连接建立失败['.$fd.']');
        }
        return false;
    }

    protected function eventLoop(\Socket $client){
        socket_set_nonblock($client);
        $timeoutSeconds = $this->config['timeout']; // 超时时间设置为5秒
        socket_set_option($client, SOL_SOCKET, SO_RCVTIMEO, ['sec' => $timeoutSeconds, 'usec' => 0]);

        for(;;){
            $msg = socket_read($client, 1024);
            if($msg === ""){
                break;
            }else{
                socket_write($client, "ping"."\r\n");
                sleep(30);
            }
        }
    }
}